package com.catena.core.chain;

import com.catena.core.CatenaOperationConstant;
import com.catena.core.GetNodeParameterFailException;
import com.catena.core.NodeParameter;
import com.catena.core.node.CatenaNode;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentSkipListMap;

/**
 * Created by hx-pc on 16-3-3.
 */
public class NodeRoot extends CatenaOperationNodeAppender implements CatenaOperationNodeStart {

    private static final String START_TYPE = CatenaOperationConstant.START_TYPE;
    private static final String TARGET = CatenaOperationConstant.TARGET;
    private static final String RETURN_DATA_TYPE = CatenaOperationConstant.RETURN_DATA_TYPE;
    private String keyMessage;
    private String linkMessage;
    private BlockingQueue<Method> methodQueue;
    private BlockingQueue<CatenaNode> nodeQueue;

    public NodeParameter getParam() {
        try {
            if (getParamClass() == null) {
                return new NodeParameter();
            }
            return (NodeParameter) getParamClass().newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new GetNodeParameterFailException(e);
        }
    }

    @Override
    public void start(Map<String, Object> map) {
        NodeParameter parameter = getParam();
        parameter.getOtherParam().putAll(map);
        parameter.getOtherParam().put(CatenaOperationConstant.START_TYPE, CatenaOperationConstant.START_TYPE_VALUE_NONE);
        this.run(parameter, methodQueue, nodeQueue);
    }

    @Override
    public void start() {
        NodeParameter parameter = getParam();
        parameter.getOtherParam().put(CatenaOperationConstant.START_TYPE, CatenaOperationConstant.START_TYPE_VALUE_NONE);
        this.run(parameter, methodQueue, nodeQueue);
    }

    @Override
    public void start(NodeParameter parameter) {
        parameter.getOtherParam().put(CatenaOperationConstant.START_TYPE, CatenaOperationConstant.START_TYPE_VALUE_NONE);
        this.run(parameter, methodQueue, nodeQueue);
    }

    @Override
    public void startForward(HttpServletRequest servletRequest, HttpServletResponse servletResponse, String target) {
        Map<String, Object> map = new ConcurrentSkipListMap<>();
        startForward(map, servletRequest, servletResponse, target);
    }

    @Override
    public void startForward(Map<String, Object> map, HttpServletRequest servletRequest, HttpServletResponse servletResponse, String target) {
        NodeParameter parameter = getParam();
        parameter.getOtherParam().putAll(map);
        parameter.buildServletResponse(servletResponse);
        parameter.buildServletRequest(servletRequest);
        parameter.getOtherParam().put(TARGET, target);
        parameter.getOtherParam().put(START_TYPE, CatenaOperationConstant.START_TYPE_VALUE_FORWARD);
        this.run(parameter, methodQueue, nodeQueue);
    }

    @Override
    public void startRedirect(HttpServletResponse servletResponse, String target) {
        Map<String, Object> map = new ConcurrentSkipListMap<>();
        startRedirect(map, servletResponse, target);
    }

    @Override
    public void startRedirect(Map<String, Object> map, HttpServletResponse servletResponse, String target) {
        NodeParameter parameter = getParam();
        parameter.getOtherParam().putAll(map);
        parameter.buildServletResponse(servletResponse);
        parameter.getOtherParam().put(TARGET, target);
        parameter.getOtherParam().put(START_TYPE, CatenaOperationConstant.START_TYPE_VALUE_REDIRECT);
        this.run(parameter, methodQueue, nodeQueue);
    }

    @Override
    public void startReturnAll(Map<String, Object> map, HttpServletResponse servletResponse) {
        NodeParameter parameter = getParam();
        parameter.getOtherParam().putAll(map);
        parameter.buildServletResponse(servletResponse);
        parameter.getOtherParam().put(START_TYPE, CatenaOperationConstant.START_TYPE_VALUE_RETURN);
        parameter.getOtherParam().put(RETURN_DATA_TYPE, CatenaOperationConstant.RETURN_DATA_TYPE_STRING);
        this.run(parameter, methodQueue, nodeQueue);
    }

    @Override
    public void startReturnAll(HttpServletResponse servletResponse) {
        NodeParameter parameter = getParam();
        parameter.buildServletResponse(servletResponse);
        parameter.getOtherParam().put(START_TYPE, CatenaOperationConstant.START_TYPE_VALUE_RETURN);
        parameter.getOtherParam().put(RETURN_DATA_TYPE, CatenaOperationConstant.RETURN_DATA_TYPE_STRING);
        this.run(parameter, methodQueue, nodeQueue);
    }

    @Override
    public void startReturnData(Map<String, Object> map, HttpServletResponse servletResponse) {
        map.put(CatenaOperationConstant.START_RETURN, CatenaOperationConstant.START_RETURN_VALUE);
        startReturnAll(map, servletResponse);
    }

    @Override
    public void startReturnData(HttpServletResponse servletResponse) {
        Map<String, Object> map = new ConcurrentSkipListMap<>();
        map.put(CatenaOperationConstant.START_RETURN, CatenaOperationConstant.START_RETURN_VALUE);
        startReturnAll(map, servletResponse);
    }

    @Override
    public void startReturnDataWithString(HttpServletRequest servletRequest,HttpServletResponse servletResponse) {
        Map<String, Object> map = new ConcurrentSkipListMap<>();
        map.put(CatenaOperationConstant.START_RETURN, CatenaOperationConstant.START_RETURN_VALUE);
        NodeParameter parameter = getParam();
        parameter.getOtherParam().putAll(map);
        parameter.buildServletResponse(servletResponse);
        parameter.buildServletRequest(servletRequest);
        parameter.getOtherParam().put(START_TYPE, CatenaOperationConstant.START_TYPE_VALUE_RETURN);
        parameter.getOtherParam().put(RETURN_DATA_TYPE, CatenaOperationConstant.RETURN_DATA_TYPE_STRING);
        this.run(parameter, methodQueue, nodeQueue);
    }

    @Override
    public void startReturnDataWithObject(HttpServletRequest servletRequest,HttpServletResponse servletResponse) {
        Map<String, Object> map = new ConcurrentSkipListMap<>();
        map.put(CatenaOperationConstant.START_RETURN, CatenaOperationConstant.START_RETURN_VALUE);
        NodeParameter parameter = getParam();
        parameter.getOtherParam().putAll(map);
        parameter.buildServletResponse(servletResponse);
        parameter.buildServletRequest(servletRequest);
        parameter.getOtherParam().put(START_TYPE, CatenaOperationConstant.START_TYPE_VALUE_RETURN);
        parameter.getOtherParam().put(RETURN_DATA_TYPE, CatenaOperationConstant.RETURN_DATA_TYPE_OBJECT);
        this.run(parameter, methodQueue, nodeQueue);
    }

    public String showNode() {
        StringBuilder sb = new StringBuilder();
        if (this.getLinkMessage() != null) {
            sb.append("key ").append(this.getKeyMessage()).append("|")
                    .append("link ").append(this.getLinkMessage());
        } else {
            sb.append("key ").append(this.getKeyMessage()).append("|")
                    .append("|").append("methods:").append(this.methodQueue).append("|");
        }
        return String.valueOf(sb);
    }

    @Override
    public void run(NodeParameter parameter, BlockingQueue<Method> methodQueue, BlockingQueue<CatenaNode> nodeQueue) {
        if (this.methodQueue == null) {
            setMethodQueue(methodQueue);
        }
        if (this.nodeQueue == null) {
            setNodeQueue(nodeQueue);
        }
        this.getNextNode().run(parameter, this.methodQueue, this.nodeQueue);
    }

    public void setMethodQueue(BlockingQueue<Method> methodQueue) {
        this.methodQueue = methodQueue;
    }

    public void setNodeQueue(BlockingQueue<CatenaNode> nodeQueue) {
        this.nodeQueue = nodeQueue;
    }

    public String getKeyMessage() {
        return keyMessage;
    }

    public NodeRoot setKeyMessage(String keyMessage) {
        this.keyMessage = keyMessage;
        return this;
    }

    public String getLinkMessage() {
        return linkMessage;
    }

    public void setLinkMessage(String linkMessage) {
        this.linkMessage = linkMessage;
    }

}
